/*
 * Copyright (c) 2011 Imre Fazekas.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the Brillien nor the names of its
 * terms and concepts may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.brillien.core.component;

import com.vii.brillien.core.component.sso.SubjectPresence;
import com.vii.brillien.core.management.BrillienError;
import com.vii.brillien.core.management.BrillienServices;
import com.vii.brillien.core.management.CouchDBServices;
import com.vii.brillien.core.management.FlowServices;
import com.vii.brillien.core.management.component.PresenceServices;
import com.vii.brillien.core.management.pool.PresenceObjectFactory;
import com.vii.brillien.ignition.BrillienContext;
import com.vii.brillien.ignition.transport.BrillienCommunication;
import com.vii.brillien.ignition.transport.BrillienMediator;
import com.vii.brillien.ignition.transport.TransportStatics;
import com.vii.brillien.kernel.BrillienException;
import com.vii.brillien.kernel.annotations.Inject;
import com.vii.brillien.kernel.annotations.PresenceService;
import com.vii.brillien.kernel.annotations.lifecycle.Consonant;
import com.vii.brillien.kernel.annotations.lifecycle.Resident;
import com.vii.brillien.kernel.annotations.lifecycle.Sparkle;
import com.vii.brillien.kernel.axiom.atomic.Flow;
import com.vii.brillien.kernel.axiom.atomic.Presence;
import com.vii.brillien.kernel.axiom.atomic.PresenceManager;
import com.vii.brillien.kernel.axiom.atomic.Unit;
import com.vii.brillien.kernel.axiom.component.StateDivision;
import com.vii.brillien.kernel.axiom.transport.Communication;
import com.vii.brillien.kernel.axiom.transport.MessageProcessor;
import com.vii.streamline.services.StringServices;
import com.vii.streamline.services.reflection.MethodSelector;
import com.vii.streamline.services.reflection.ReflectionServices;
import com.vii.streamline.structures.collections.InnerList;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.StackObjectPool;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;

import static com.vii.brillien.ignition.BrillienContext.INITIAL_PRESENCE_COUNT;
import static com.vii.brillien.ignition.BrillienContext.MAX_IDLE_PRESENCE_COUNT;

/**
 * Parent class for every PresenceManager in Brillien
 */
@PresenceService( logLevel = PresenceService.FINE )
public class AbstractPresenceManager <P extends Presence> extends SuperUnit<P, Object> implements PresenceManager<P, BrillienCommunication> {

    /**
     * Presence type to be managed
     */
    protected Class<P>                      PresenceClass;

    /**
     * Resident instance if exists
     */
    protected P                             resident;

    /**
     * Consonant instances created and associated to Flow instances
     */
    protected ConcurrentHashMap<String,P>   consonants;

    /**
     * Sparkle instances
     */
    protected ConcurrentLinkedQueue<P>      sparkles;

    /**
     * Instantiation policy of the presence
     */
    protected int                           managementType;

    /**
     * The name of the Flow where the managed Presence belongs
     */
    protected String                        flowIDPrefix;

    /**
     * Flow association modifier, according to the annotation Sparkle  
     */
    protected boolean                       dorky;

    /**
     * Log type of the managed Presence
     */
    protected int                           logType;

    /**
     * Log level of the managed Presence
     */
    protected Level                         logLevel;

    /**
     * Message expiration timeout
     */
    protected long                          messageExpiration;

    /**
     * Classloader of the managed Presence
     */
    protected ClassLoader                   classLoader;

    /**
     * Initial parameters of the presence defined by the deployment description
     */
    protected Map<String, Object>           parameters;

    /**
     * Timeout measure of the Presence type
     */
    protected long                          presenceTimeMeasure;

    /**
     * Periodicity of the Presemce type 
     */
    protected boolean                       presencePeriodical;

    /**
     * ObjectPool to help the instantiation of the managed Presence
     */
    protected ObjectPool                    pool;

    /**
     * Poolfactory to help the instantiation of the managed Presence
     */
    protected PoolableObjectFactory         objectFactory;

    /**
     * Mediator instance handling all incoming message addressed to this Presence type
     */
    protected BrillienMediator              mediator;

    protected String                        ssoPresenceName;
    
    
    
    protected ConcurrentHashMap<String, BrillienCommunication>          incomings;
    protected ConcurrentHashMap<String, ConcurrentLinkedQueue<String>>  outGoings;
    protected ConcurrentHashMap<String, String>                         outGoingRefers;


    {
        sparkles        = new ConcurrentLinkedQueue<P>();
        consonants      = new ConcurrentHashMap<String,P>();

        classLoader     = this.getClassLoader();

        logLevel        = BrillienContext.DEFAULT_LEVEL;

        timeMeasure     = -1;

        flow            = BrillienServices.SERVICES_FLOW;

        ssoPresenceName = null;

        incomings       = new ConcurrentHashMap<String, BrillienCommunication>();
        outGoings       = new ConcurrentHashMap<String, ConcurrentLinkedQueue<String>>();
        outGoingRefers  = new ConcurrentHashMap<String, String>();
    }

    @Override
    public void reset(){
        super.reset();
        needExtraLog = true;
    }


    public class PresenceMessageProcessor implements MessageProcessor<BrillienCommunication> {
        private BrillienCommunication comm;
        @Override
        public void init(BrillienCommunication comm) {
            this.comm = comm;
        }

        private Object errorReceived( BrillienCommunication msg ) throws BrillienException {
            Presence p = getInstance(
                (msg.getFlowID() == null || msg.getFlowID().equals( Communication.DEFAULT_FLOW_ID )) ? BrillienContext.SERVICES_FLOW_ID : msg.getFlowID(), null
            );
            return p.errorReceived( msg );
        }

        private Object communicationReceived( BrillienCommunication msg ) throws BrillienException {
            Presence p = getInstance(
                (msg.getFlowID() == null || msg.getFlowID().equals( Communication.DEFAULT_FLOW_ID )) ? BrillienContext.SERVICES_FLOW_ID : msg.getFlowID(), null
            );

            return p.communicationReceived( msg );
        }

        public Object processCall( ) throws BrillienException {
            Object result = null;
            if( comm.getMessageType() == Communication.TYPE_ERROR ){
                log( "Processing error-type message." );
                result = errorReceived( comm );
            }
            else if( comm.getMessageType() == Communication.TYPE_RESULT ){
                log( "Processing result-type message." );
                if( comm.getOriginalType() == Communication.TYPE_GET ){
                    // already processed
                }
                else if( comm.getOriginalType() == Communication.TYPE_AGET ){
                    result = communicationReceived( comm );
                }
            } 
            else if( comm.getMessageType() == Communication.TYPE_GET ||
                    comm.getMessageType() == Communication.TYPE_AGET ||
                    comm.getMessageType() == Communication.TYPE_DELEGATED_SET ||
                    comm.getMessageType() == Communication.TYPE_SET ){
                log(  "Processing Get/AGet/DSet/Set message : " + comm.getSubject() );
                communicationReceived( comm );
            }   
            return result;
        }

        @Override
        public void run() {
            if( comm.getId() == null )
                comm.setId( TransportStatics.newMessageId() );

            log("Message received. Id: " + comm.getId() + " OriginalId: " + comm.getOriginalId() + " Sender: " + comm.getSender() + " Recipient:" + comm.getRecipient() + " Subject:" + comm.getSubject() );

            String sourceId = null;
            if( comm.getMessageType() == Communication.TYPE_GET || comm.getMessageType() == Communication.TYPE_AGET ){
                incomings.putIfAbsent(comm.getId(), comm);
                ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
                queue.add( comm.getId() );
                outGoings.putIfAbsent( comm.getId(), queue );
            }
            else if( comm.getMessageType() == Communication.TYPE_ERROR || comm.getMessageType() == Communication.TYPE_RESULT ){
                if( outGoingRefers.containsKey( comm.getOriginalId() ) ){
                    sourceId = outGoingRefers.get(comm.getOriginalId());
                }
            }

            BrillienServices.SERVICES_FLOW.registerSharing( AbstractPresenceManager.this, Thread.currentThread(),
                    sourceId != null ? incomings.get( sourceId ) : comm
            );

            Object result = null;
            try {
                result = processCall( );
            } catch (Throwable t) {
                exceptionLog( this.getClass().getName(), "run", t );
            }

            BrillienServices.SERVICES_FLOW.removeSharing( AbstractPresenceManager.this, Thread.currentThread() );

            if( comm.getMessageType() == Communication.TYPE_GET || comm.getMessageType() == Communication.TYPE_AGET ){
                outGoings.get( comm.getId() ).remove( comm.getId() );
            } else if( comm.getMessageType() == Communication.TYPE_ERROR || comm.getMessageType() == Communication.TYPE_RESULT ){
                outGoingRefers.remove( comm.getOriginalId() );
                if( sourceId != null )
                    outGoings.get( sourceId ).remove( comm.getOriginalId() );
            }

            if( sourceId != null && outGoings.get(sourceId).isEmpty() ){
                BrillienCommunication source = incomings.get( sourceId );
                try{
                    Presence p = getInstance(
                            (source.getFlowID() == null || source.getFlowID().equals( Communication.DEFAULT_FLOW_ID )) ? BrillienContext.SERVICES_FLOW_ID : source.getFlowID(), null
                    );

                    if(
                            result instanceof BrillienError
                            ){
                        log( "Closing by error message." );
                        BrillienError error = (BrillienError)result;
                        p.sendError( source, error.getCode(), error.getMessage(), error.getValue() );
                    }
                    else{
                        log( "Closing by response message.", new InnerList<Object>( source, result ) );
                        p.sendResponse(
                                source,
                                result != null && result != BrillienServices.NULL_ANSWER
                                        ? result
                                        : null
                        );
                    }
                } catch (Throwable t) {
                    exceptionLog( this.getClass().getName(), "run", t );
                } finally {
                    incomings.remove( sourceId );
                    outGoings.remove( source );
                }
            }
        }
    }

    @Override
    public void initPresenceManager( String presenceName, Class<P> PresenceClass ) throws BrillienException {

        if( !PresenceClass.isAnnotationPresent( PresenceService.class ) )
            throw new BrillienException("PresenceService annotation must be present!");

        this.PresenceClass = PresenceClass;

        managementType =    PresenceClass.isAnnotationPresent( Sparkle.class )
                            ? Presence.SPARKLE
                            : PresenceClass.isAnnotationPresent( Consonant.class )
                            ? Presence.CONSONANT
                            : PresenceClass.isAnnotationPresent( Resident.class )
                            ? Presence.RESIDENT
                            : Presence.SPARKLE;

        if( PresenceClass.isAnnotationPresent( Sparkle.class ) ){
            dorky = PresenceClass.getAnnotation( Sparkle.class ).dorky();
        }

        if( managementType != Presence.RESIDENT ){
            objectFactory = new PresenceObjectFactory( PresenceClass );
            pool = new StackObjectPool( objectFactory, MAX_IDLE_PRESENCE_COUNT, INITIAL_PRESENCE_COUNT );
        }


        flowIDPrefix =   managementType == Presence.CONSONANT
                                ? PresenceClass.getAnnotation( Consonant.class ).flowIDPrefix()
                                : "";

        this.logType = PresenceService.DEFAULT_LOG_TYPE;

        PresenceService service = PresenceClass.getAnnotation( PresenceService.class );
        this.setName( presenceName == null 
                ? ( service == null ? PresenceClass.getSimpleName() : PresenceServices.getPresenceName( PresenceClass ) )
                : presenceName
        );
        this.setLogType( service == null ? PresenceService.DEFAULT_LOG_TYPE : service.logType() );
        this.setLogLevel( service == null ? PresenceService.DEFAULT_LOG_LEVEL : service.logLevel() );
        this.setPresenceTimeMeasure( service == null ? PresenceService.DEFAULT_TIMEMEASURE : service.timeMeasure() );
        this.setPresencePeriodical( service == null ? false : service.periodical() );
        this.setMessageExpiration( service.messageExpiration() );
        try{
            this.setStateDivision( service == null ? null : (StateDivision)Class.forName( service.stateDivision() ).newInstance() );
        } catch( Exception e ){  }

        if( BrillienContext.WEBSERVER_ON && !service.internal() ){
            try{
                final Class presenceClass = PresenceClass;
                List<Method> methods = ReflectionServices.getServiceMethods( PresenceClass, new MethodSelector() {
                    @Override
                    public boolean isMethodConvenient(Method m) {
                        return !PresenceManager.class.isAssignableFrom(presenceClass) &&
                            SuperPresence.class.isAssignableFrom( m.getDeclaringClass() ) &&
                            ( !m.getDeclaringClass().getName().startsWith( "com.vii.brillien" ) || SubjectPresence.class.isAssignableFrom(m.getDeclaringClass()) ) &&
                            !m.getDeclaringClass().equals(SuperPresence.class) &&
                            !m.getDeclaringClass().equals(AbstractContext.class) &&
                            !m.getDeclaringClass().equals(SuperFlow.class) &&
                            !m.getDeclaringClass().equals(SuperUnit.class);
                    }
                });
                for( Method m : methods ){
                    try{
                        ReflectionServices.getFormalParameterNames( m );
                    } catch (Exception e){
                        exceptionLog( this.getClass().getName(), "initPresenceManager", e );
                    }

                    log("Published message processor: " + m.getName() + " associated with: " + this.getName() );
                    BrillienServices.addPublisher(this.getName(), m.getName());
                }
            } catch (Exception e){
                log("Error while discovering service methods:" + e.getMessage() );
            }
        }
    }

    @Override
    public void activatePresenceManager( ) throws BrillienException{
        mediator = BrillienContext.newMediator( this.getName(), this.getName() );
        mediator.connect();
        mediator.addMessageProcessorType( PresenceMessageProcessor.class, this );
    }

    @Override
    public long getPresenceTimeMeasure() {
        return presenceTimeMeasure;
    }

    @Override
    public void setPresenceTimeMeasure(long presenceTimeMeasure) {
        this.presenceTimeMeasure = presenceTimeMeasure;
    }

    @Override
    public boolean isPresencePeriodical() {
        return presencePeriodical;
    }

    @Override
    public void setPresencePeriodical(boolean presencePeriodical) {
        this.presencePeriodical = presencePeriodical;
    }

    @Override
    public long getMessageExpiration() {
        return messageExpiration;
    }

    @Override
    public void setMessageExpiration(long messageExpiration) {
        this.messageExpiration = messageExpiration;
    }

    @Override
    public BrillienMediator getMediator() {
        return mediator;
    }

    @Override
    public String getFlowIDPrefix(){
        return  flowIDPrefix;
    }

    @Override
    public int getLogType() {
        return logType;
    }

    @Override
    public void setLogType(int logType) {
        this.logType = logType;
    }

    @Override
    public Level getLogLevel() {
        return logLevel;
    }

    @Override
    public void setLogLevel(String logLevel) {
        try {
            this.logLevel = ReflectionServices.getFieldValue(Level.class, Level.class, logLevel);
        } catch (Exception e) {
            exceptionLog( "AbstractPresenceManager", "setLogLevel", e );
            //e.printStackTrace();
        }
    }

    @Override
    public int getManagementType() {
        return managementType;
    }

    @Override
    public Map<String, Object> getParameters() {
        return parameters;
    }

    @Override
    public void setParameters(Map<String, Object> parameters){
        this.parameters = parameters;
    }

    @Override
    public ClassLoader getClassLoader( ){
        return this.classLoader;
    }

    @Override
    public void setClassLoader(ClassLoader classLoader){
        this.classLoader = classLoader;
    }

    @Override
    public Class<P> getPresenceClass() {
        return PresenceClass;
    }


    /**
     * Fills the Presence instance by parameters coming from deployment descriptor
     */
    protected void addFieldValue( P s, String fieldName, Object value ){
        try {
            ReflectionServices.setFieldValue(
                    s.getClass(), s, fieldName.toString(), value
            );
        } catch (Exception e) {
            try {
                ReflectionServices.setFieldValueThroughSetMethod(
                    s.getClass(), s, fieldName.toString(), value
                );
            } catch (Exception e1) {
                exceptionLog( AbstractPresenceManager.class.getSimpleName(), "addFieldValue", new BrillienException( "Field " + fieldName + " is not public and has no set method" ) );
            }
        }
    }

    @Override
    public String getFullName(){
        return "Manager of " + name;
    }

    /**
     * Performs all possible injections on the given presence instance
     */
    protected void performInjections( P s ){
        InnerList<Field> fields = new InnerList<Field>(
                ReflectionServices.<Field, Inject>getAnnotatedFields( Inject.class,  PresenceClass ),
                ReflectionServices.<Field, Inject>getAnnotatedDeclaredFields( Inject.class,  PresenceClass )
        );
        for( Field f : fields ){
            Inject inject = f.getAnnotation( Inject.class );
            String name = inject.sharedName();
            if( name == null || name.length() == 0 )
                name = f.getName();

            Object sharing = getSharing( name );
            if( sharing == null && !inject.nullAllowed() )
                exceptionLog( this.getClass().getName(), "performInjections", new BrillienException("No shared resource has been found by this name: " + name )  );

            if( sharing != null )
                try {
                    ReflectionServices.setFieldValue( PresenceClass, s, f.getName(), sharing );
                } catch (Exception e) {
                    try {
                        ReflectionServices.setFieldValueThroughSetMethod(PresenceClass, s, f.getName(), sharing);
                    } catch (Exception e1) {
                        exceptionLog( this.getClass().getName(), "performInjections", new BrillienException("Shared value cannot be set for field (access limitations): " + f.getName() )  );
                    }
                }
        }
    }

    /**
     * Initializes a Presence instance
     */
    protected void initializePresenceInstance( P s, Flow flow, String name ) throws BrillienException {
        s.setName( name == null
            ? getName() + BrillienServices.newNamePostfix() 
            : name
        );
        s.setFlow( flow );
        s.setPresenceManager( this );
        s.setStateDivision( getStateDivision() );
        s.setAspirationTimeMeasure( getPresenceTimeMeasure() );
        s.setAspirationPeriodical( isPresencePeriodical() );

        if( parameters != null )
            for( String fieldName : parameters.keySet() )
                addFieldValue( s, fieldName, parameters.get( fieldName ) );

        try{
            performInjections( s );
        } catch(Exception ex){
            exceptionLog( this.getClass().getName(), "initializePresenceInstance", new BrillienException("Internal error occurred performing injections", ex )  );
        }

        s.activateAll();

        s.plan();
    }

    @Override
    public P getInstance(Presence caller) throws BrillienException {
        return getInstance( caller, null );
    }

    @Override
    public P getInstance(Presence caller, String name) throws BrillienException {
        P t = null;
        if( caller == null || ( !(caller instanceof Flow) && caller.getFlow() == null) )
            throw new BrillienException("Caller and Container flow existence is required!");

        log( "Request for get an instance of " + PresenceClass + " by caller::" + caller);

        try{
            switch( managementType ){
                case Presence.RESIDENT : t = getResidentInstance( name ); break;
                case Presence.CONSONANT : t = getConsonantInstance( caller, name ); break;
                case Presence.SPARKLE: t = getSparkleInstance( caller, name ); break;
                default : t = getResidentInstance( name ); break;
            }
        }catch( Exception e ){
            exceptionLog( "AbstractPresenceManager", "getInstance managementtype", e );
        }

        return t;
    }

    @Override
    public P getInstance(String flowID) throws BrillienException {
        return getInstance( flowID, null );    
    }

    @Override
    public P getInstance( String flowID, String name ) throws BrillienException {
        P t = null;
        if( flowID == null && managementType != Presence.RESIDENT )
            throw new BrillienException("Proper flow specification is required!");

        log( "Request for get an instance for::" + flowID + " of " + getName() + " " + flowIDPrefix );
        
        try{
            switch( managementType ){
                case Presence.RESIDENT : t = getResidentInstance( name ); break;
                case Presence.CONSONANT : t = getConsonantInstance( flowID, name ); break;
                case Presence.SPARKLE: t = getSparkleInstance( flowID, name ); break;
                default : t = getResidentInstance( name ); break;
            }
        }catch( Exception e ){
            e.printStackTrace();
            exceptionLog( "AbstractPresenceManager", "getInstance managementtype", e );
        }
        return t;
    }

    /**
     * Gets a new Resident instance
     */
    protected P getResidentInstance( String name ) throws Exception {
        log( "Creating resident instance: " + PresenceClass );

        if( resident == null ){
            P t = PresenceClass.newInstance();

            initializePresenceInstance( t, BrillienServices.SERVICES_FLOW, name == null ? getName() : name );

            BrillienServices.SERVICES_FLOW.registerSharing( this, t.getName(), t );

            addPresences( resident = t );
        }
        else if( name!=null && !resident.getName().equals( name ) )
            throw new BrillienException("Instance already created with different name!" + resident.getName() + " " + name );
        
        return resident;
    }

    /**
     * Gets a new Consonant instance
     */
    protected P getConsonantInstance( Presence caller, String name ) throws Exception {
        log( "Getting consonant instance: " + PresenceClass + " by the request of " + caller + " in flow: " + caller.getFlow() + " " );

        Flow referenceFlow = caller instanceof Flow ? (Flow)caller : caller.getFlow();

        PresenceManager pm = BrillienServices.liaison.getPresenceManagerOf( this.flowIDPrefix );

        if( this.flowIDPrefix != null && this.flowIDPrefix.length() > 0 ){
            while( referenceFlow != null ){
                if( referenceFlow.getName().equals( this.flowIDPrefix ) || referenceFlow.getFlowID().equals( this.flowIDPrefix ) ){
                    break;
                }

                if(
                    pm!= null &&
                    (
                        referenceFlow.getName().equals(pm.getFlowIDPrefix() ) ||
                        referenceFlow.getFlowID().equals( pm.getFlowIDPrefix())
                    )
                ){
                    Flow f = (Flow) BrillienServices.getPresenceByName(caller, flowIDPrefix );
                    f.setFlow( referenceFlow );
                    referenceFlow = f;
                    break;
                }

                referenceFlow = referenceFlow.getFlow();
            }
        }

        if( referenceFlow == null )
            throw new BrillienException( "Cannot determine parent flow. This Presence requires this Flow type: " + flowIDPrefix );

        if( consonants.containsKey( referenceFlow.getFlowID() ) ){
            if( name!=null && !consonants.get( referenceFlow.getFlowID() ).getName().equals( name ) )
                throw new BrillienException("Instance already created with different name!" + consonants.get( referenceFlow.getFlowID() ).getName() + " " + name );

            log( "Reusing consonant instance: " + PresenceClass + " by the request of " + caller + " in flow: " + caller.getFlow() );
        }
        else{
            log( "Creating consonant instance: " + PresenceClass + " by the request of " + caller + " in flow: " + caller.getFlow() );

            P s = (P)pool.borrowObject();

            initializePresenceInstance( s, referenceFlow, name );

            addPresences( s );
            if( s instanceof Unit )
                referenceFlow.addSubUnits( (Unit)s );
            else
                referenceFlow.addPresences( s );


            consonants.put( referenceFlow.getFlowID(), s );
        }

        return consonants.get( referenceFlow.getFlowID() );
    }

    /**
     * Gets a new sparkle instance
     */
    protected P getSparkleInstance( Presence caller, String name ) throws Exception {
        log( "Creating sparkle instance: " + PresenceClass.getSimpleName() + " " + sparkles.size() + " worker(s) in the queue" );

        Flow flow = dorky ? BrillienServices.SERVICES_FLOW : ( caller instanceof Flow ? (Flow)caller : caller.getFlow() );

        P s = null;
        try{
            s = (P)pool.borrowObject();
        } catch(Throwable t){
            throw new BrillienException( t.getMessage(), t );
        }
        
        initializePresenceInstance( s, flow, name );

        addPresences( s );

        sparkles.add( s );

        return s;
    }

    /**
     * Gets a new Consonant instance
     */
    protected P getConsonantInstance( String flowID, String name ) throws Exception {
        Flow flow = FlowServices.getFlow(flowID);

        if( flow == null )
            throw new BrillienException( "No flow has been found with the given ID:" + flowID );

        return getConsonantInstance( flow, name );
    }

    /**
     * Gets a new sparkle instance
     */
    protected P getSparkleInstance( String flowID, String name ) throws Exception {
        Flow flow = dorky ? BrillienServices.SERVICES_FLOW : FlowServices.getFlow(flowID);

        if( flow == null )
            throw new BrillienException( "No flow has been found with the given ID:" + flowID );

        return getSparkleInstance( flow, name );
    }


    /**
     * By closing a flow, every unnecessary Presence instance will be retrieved using this method.
     * The instance is put back into the object pool.
     * @param t Presence to be retrieved
     */
    protected void retrieveReference( P t ) throws BrillienException {
        if( t.hasActivity() )
            t.passivateCallable();        
        try {
            pool.returnObject( t );
        } catch (Exception e) {
            exceptionLog( "AbstractPresenceManager", "retrieveReference", e );
        }
    }

    @Override
    public void retrieveReferences(P... presences) throws BrillienException {
        log( "Retrieve references ::" + StringServices.stringify( presences ) );
        for( P presence : presences ){
            if( consonants.containsValue(presence) ){
                for( String f : consonants.keySet() ){
                    P t = consonants.get( f );
                    if( t.equals( presence ) ){
                        log( "Retrieve consonants reference ::" + t.getName() + " in flow " + t.getFlowID() );
                        consonants.remove( f );
                        retrieveReference( t );
                        break;
                    }
                }
            }
            else for( P t : sparkles ){
                if( t.equals( presence ) ){
                    log( "Retrieve sparkle reference ::" + t.getName() + " in flow " + t.getFlowID() );
                    sparkles.remove( t );
                    retrieveReference( t );
                    break;
                }
            }
        }
        removePresences( presences );
    }


    @Override
    public void passivateCallable() throws BrillienException {
        super.passivateCallable();
        mediator.disconnect();
    }

    @Override
    public String getSSOPresenceName(){
        return ssoPresenceName == null ? (BrillienServices.liaison != null ? BrillienServices.liaison.getSSOPresenceName() : null) : ssoPresenceName;
    }

    @Override
    public void setSSOPresenceName(String ssoPresenceName) {
        this.ssoPresenceName = ssoPresenceName;
    }

    @Override
    public void markMessageToSend(BrillienCommunication shared, BrillienCommunication message){
        if( shared != null && outGoings.containsKey( shared.getId() ) ){
            //if( message.getMessageType() != Communication.TYPE_GET && message.getMessageType() != Communication.TYPE_AGET ) return;

            outGoings.get( shared.getId() ).add( message.getId() );

            outGoingRefers.put( message.getId(), shared.getId() );
        }
    }

}
